package com.asen.commons.core.meta.dao;

import com.asen.commons.core.base.dto.CoreDto;
import com.asen.commons.core.base.dto.QueryDto;
import com.asen.commons.core.meta.entity.EntityField;
import com.asen.commons.core.meta.enums.FilterRelationEnum;
import com.asen.commons.core.meta.enums.FilterSymbolEnum;
import com.asen.commons.core.meta.listener.PropertyProcessListener;
import com.asen.commons.core.utils.EntityUtils;
import com.asen.commons.core.utils.FieldUtils;
import com.asen.commons.utils.BaseAssert;
import com.asen.commons.utils.EnumUtils;
import com.asen.commons.utils.StringUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 条件工具类
 *
 * @author ls
 * @version 1.0
 * @date 2024-04-09
 */
public class ConditionBuilder {

    private static final String BEGIN = "BEGIN";

    private static final String END = "END";

    private static final String AND = "$AND";

    private static final String OR = "$OR";

    private Condition condition;

    public ConditionBuilder(Condition condition) {
        BaseAssert.notNull(condition, "condition参数不能为空");
        this.condition = condition;
    }

    public <T> Condition build(QueryDto<T> queryDto) throws Exception {
        condition.setPage(queryDto.getIsPage());
        condition.setPageNum(queryDto.getPageNum());
        condition.setPageSize(queryDto.getPageSize());

        if (StringUtils.isNotEmpty(queryDto.getCollectBy())) {
            Arrays.asList(queryDto.getCollectBy().split(",")).forEach(item -> {
                condition.collectBy(item);
            });
        }

        if (StringUtils.isNotEmpty(queryDto.getSortName())) {
            String sortName = queryDto.getSortName();
            if ("desc".equalsIgnoreCase(queryDto.getOrder())) {
                condition.orderByDesc(sortName);
            } else {
                condition.orderByAsc(sortName);
            }
        }

        if (StringUtils.isNotEmpty(queryDto.getOrderByAsc())) {
            condition.orderByAsc(queryDto.getOrderByAsc().split(","));
        }

        if (StringUtils.isNotEmpty(queryDto.getOrderByDesc())) {
            condition.orderByDesc(queryDto.getOrderByDesc().split(","));
        }

        build(queryDto.getData());

        // 根据关键字匹配
        build(queryDto.getParams());

        return condition;
    }

    /**
     * 根据关键词匹配，没有关键词的默认不做任何处理
     *
     * @param params
     */
    public Condition build(Map<String, Object> params) {
        if (params == null || params.isEmpty()) {
            return condition;
        }

        for (Map.Entry<String, Object> entry : params.entrySet()) {
            autoBuild(entry.getKey(), entry.getValue());
        }

        return condition;
    }

    public Condition build(CoreDto coreDto) {
        Set<Long> ids = new HashSet<>();

        if (coreDto.getId() != null) {
            ids.add(coreDto.getId());
        }

        if (coreDto.getIds() != null) {
            ids.addAll(coreDto.getIds());
        }

        condition.in("id", ids);

        return condition;
    }

    /**
     * 关键词匹配（以【$】分隔）
     *
     * @param key
     * @param value
     */
    private void autoBuild(String key, Object value) {
        int index = key.indexOf("$");
        if (index == -1) {
            return;
        }

        if (OR.equals(key) && value instanceof Map) {
            FilterGroup filterGroup = new FilterGroup(FilterRelationEnum.OR);
            condition.addFilterGroup(filterGroup);
            Map<String, Object> map = (Map<String, Object>) value;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                autoBuild(filterGroup, entry.getKey(), entry.getValue());
            }
            return;
        }

        if (AND.equals(key) && value instanceof Map) {
            FilterGroup filterGroup = new FilterGroup(FilterRelationEnum.AND);
            condition.addFilterGroup(filterGroup);
            Map<String, Object> map = (Map<String, Object>) value;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                autoBuild(filterGroup, entry.getKey(), entry.getValue());
            }
            return;
        }

        String fieldName = key.substring(0, index);
        String compare = key.substring(index + 1);

        if (BEGIN.equals(compare)) {
            condition.greaterEqual(fieldName, getDateValue(value, true));
            return;
        }

        if (END.equals(compare)) {
            condition.lessEqual(fieldName, getDateValue(value, false));
            return;
        }

        FilterSymbolEnum filterSymbol = EnumUtils.valueOfName(FilterSymbolEnum.class, compare);
        if (filterSymbol == null) {
            return;
        }

        condition.addFilterItem(fieldName, filterSymbol, value);
    }

    private void autoBuild(FilterGroup filterGroup, String key, Object value) {
        int index = key.indexOf("$");
        if (index == -1) {
            return;
        }

        if (OR.equals(key) && value instanceof Map) {
            FilterGroup subGroup = new FilterGroup(FilterRelationEnum.OR);
            filterGroup.addFilterGroup(subGroup);
            Map<String, Object> map = (Map<String, Object>) value;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                autoBuild(subGroup, entry.getKey(), entry.getValue());
            }
            return;
        }

        if (AND.equals(key) && value instanceof Map) {
            FilterGroup subGroup = new FilterGroup(FilterRelationEnum.AND);
            filterGroup.addFilterGroup(subGroup);
            Map<String, Object> map = (Map<String, Object>) value;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                autoBuild(subGroup, entry.getKey(), entry.getValue());
            }
            return;
        }

        String fieldName = key.substring(0, index);
        String compare = key.substring(index + 1);

        if (BEGIN.equals(compare)) {
            filterGroup.greaterEqual(fieldName, getDateValue(value, true));
            return;
        }

        if (END.equals(compare)) {
            filterGroup.lessEqual(fieldName, getDateValue(value, false));
            return;
        }

        FilterSymbolEnum filterSymbol = EnumUtils.valueOfName(FilterSymbolEnum.class, compare);
        if (filterSymbol == null) {
            return;
        }

        filterGroup.addFilterItem(fieldName, filterSymbol, value);
    }

    private Object getDateValue(Object value, boolean isBegin) {
        if (value == null) {
            return null;
        }

        Object date = null;

        if (value instanceof Date) {
            date = (Date) value;
        } else if (String.class.isAssignableFrom(value.getClass()) && StringUtils.isNotEmpty((String) value)) {
            String s = (String) value;

            // 日期格式："yyyy-MM-dd"
            if (StringUtils.isDate(s)) {
                if (isBegin) {
                    s = s + " 00:00:00";
                } else {
                    s = s + " 23:59:59";
                }

                date = s;
            }

            // 日期格式："yyyy-MM-dd HH:mm:ss"
            if (StringUtils.isDateTime(s)) {
                date = s;
            }
        }

        return date;
    }

    /**
     * DTO转换为condition条件
     * <p>转换值不为空的属性，默认比较关系为equal，属性之间以AND连接（子类可以覆盖重写）</p>
     *
     * @return
     */
    public Condition build(Object model) throws Exception {
        return build(model, null);
    }

    /**
     * DTO转换为condition条件
     * <p>转换值不为空的属性，默认比较关系为equal，属性之间以AND连接（子类可以覆盖重写）</p>
     *
     * @return
     */
    public Condition build(Object model, PropertyProcessListener listener) throws Exception {
        if (model == null) {
            return condition;
        }

        List<EntityField> fieldList = FieldUtils.getFieldList(model.getClass());
        for (EntityField ef : fieldList) {
            if (listener != null) {
                if (listener.processed(ef.getName())) {
                    continue;
                }
            }

            //忽略static和transient字段
            if (EntityUtils.isStaticOrTransient(ef.getField())) {
                continue;
            }

            Object value = ef.getValue(model);
            if (value == null) {
                continue;
            }

            // 忽略其它集合类型
            if (Map.class.isAssignableFrom(ef.getJavaType())
                    || Collection.class.isAssignableFrom(ef.getJavaType())) {
                continue;
            }

            build(ef, value);
        }

        return condition;
    }

    private void build(EntityField ef, Object value) throws Exception {
        Field field = ef.getField();

        String fieldName = field.getName();
        if (EntityUtils.isSimpleType(ef.getJavaType())) {
            // 空字符串不作为条件
            if (value instanceof String && StringUtils.isEmpty((String) value)) {
                return;
            }
            condition.equal(fieldName, value);
            return;
        }

        List<EntityField> fieldList = FieldUtils.getFieldList(ef.getJavaType());

        build(fieldList, value, fieldName + ".");
    }

    private void build(List<EntityField> fieldList, Object model, String prefix) throws Exception {
        for (EntityField ef : fieldList) {
            Object value = ef.getValue(model);

            //忽略static和transient字段
            if (EntityUtils.isStaticOrTransient(ef.getField())) {
                continue;
            }

            // 忽略集合类型
            if (Map.class.isAssignableFrom(ef.getField().getType())
                    || Collection.class.isAssignableFrom(ef.getField().getType())) {
                continue;
            }

            String fieldName = ef.getName();
            if (EntityUtils.isSimpleType(ef.getJavaType())) {
                condition.equal(prefix + fieldName, value);
                continue;
            }

            prefix += fieldName + ".";

            fieldList = FieldUtils.getFieldList(ef.getJavaType());

            build(fieldList, value, prefix);
        }
    }

}
